package org.witsoft.dtx.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.witsoft.dtx.domain.BranchStatus;
import org.witsoft.dtx.domain.BranchTransaction;
import org.witsoft.dtx.domain.GlobalStatus;
import org.witsoft.dtx.service.BranchTransactionService;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

/**
 * @author XiaoQing Lin
 * @date 2020-09-21 10:36
 */
public class BranchTransactionServiceImpl implements BranchTransactionService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void add(BranchTransaction entity) {
        String sql = "INSERT INTO dtx_branch(id,gid,bid,status,lock_flag,handler_class,handler_args,create_time,update_time) " +
                "VALUES(?,?,?,?,?,?,?,?,?)";;
        entity.setStatus(BranchStatus.PREPARE);
        entity.setCreateTime(new Timestamp(System.currentTimeMillis()));
        entity.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        jdbcTemplate.update(sql,
                buildUUID(entity),
                entity.getGid(),
                entity.getBid(),
                entity.getStatus().getValue(),
                false,
                entity.getHandlerClass(),
                entity.getHandlerArgs(),
                entity.getCreateTime(),
                entity.getUpdateTime()
        );
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void updateStatus(BranchTransaction entity) {
        String sql = "UPDATE dtx_branch SET status=?, update_time=? WHERE id=?";
        jdbcTemplate.update(sql,
                entity.getStatus().getValue(),
                new Date(),
                buildUUID(entity)
        );
    }

    @Override
    public List<BranchTransaction> findByGid(String gid) {
        String sql = "SELECT * FROM dtx_branch WHERE gid = ?";
        return jdbcTemplate.query(sql, new RowMapper<BranchTransaction>() {
            @Override
            public BranchTransaction mapRow(ResultSet rs, int i) throws SQLException {
                return innerMapRow(rs);
            }
        }, gid);
    }

    @Override
    public List<BranchTransaction> findUnlockByStatus(BranchStatus status , int size) {
        String sql = "SELECT * FROM dtx_branch WHERE status = ? and lock_flag=0 order by update_time limit " + size;
        return jdbcTemplate.query(sql, new RowMapper<BranchTransaction>() {
            @Override
            public BranchTransaction mapRow(ResultSet rs, int i) throws SQLException {
                return innerMapRow(rs);
            }
        }, status.getValue());
    }

    @Override
    public boolean tryLock(String gid, String bid) {
        String id = buildUUID(gid, bid);
        long timestamp = System.currentTimeMillis() ;
        String sql = "UPDATE dtx_branch SET lock_flag=1,lock_num=lock_num+1,lock_time=?,update_time=? WHERE id=? and lock_flag=0 and lock_num < 10";
        return jdbcTemplate.update(sql,
                timestamp,
                new Date(),
                id
        ) > 0;
    }

    @Override
    public boolean unlock(String gid, String bid) {
        String id = buildUUID(gid, bid);
        long timestamp = System.currentTimeMillis() ;
        String sql = "UPDATE dtx_branch SET lock_flag=0,lock_time=?,update_time=? WHERE id=? ";
        return jdbcTemplate.update(sql,
                timestamp,
                new Date(),
                id
        ) > 0;
    }

    @Override
    public int releaseLockTimeout() {
        long timestamp = System.currentTimeMillis() ;
        String sql = "update dtx_branch set lock_flag = 0 where status=1 and lock_flag = 1 and lock_num < 10 " +
                "and lock_time + POW(2,lock_num-1) * 5 * 60 * 1000  < " + timestamp;
        return jdbcTemplate.update(sql);
    }

    @Override
    public int updateConfirmedToPrepare() {
        String sql = "update dtx_branch b inner join dtx_global g " +
                " on b.gid = g.id " +
                " set b.status = " + BranchStatus.PREPARE.getValue() +
                " where b.status = " + BranchStatus.CONFIRMED.getValue() +
                " and g.status = " + GlobalStatus.CANCELING.getValue();
        return jdbcTemplate.update(sql);
    }

    private BranchTransaction innerMapRow(ResultSet rs) throws SQLException {
        String gid = rs.getString("gid");
        String bid = rs.getString("bid");
        BranchTransaction entity = new BranchTransaction(gid, bid);
        entity.setStatus(BranchStatus.of(rs.getInt("status")));
        entity.setHandlerClass(rs.getString("handler_class"));
        entity.setHandlerArgs(rs.getString("handler_args"));
        entity.setCreateTime(rs.getTimestamp("create_time"));
        entity.setUpdateTime(rs.getTimestamp("update_time"));
        return entity;
    }

    private String buildUUID(BranchTransaction entity){
        return buildUUID(entity.getGid() ,  entity.getBid());
    }

    private String buildUUID(String gid, String bid){
        String uuid = DigestUtils.md5DigestAsHex((gid + "_" + bid).getBytes());
        return uuid.substring(8, 23);
    }
}
